JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.
In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript prototypes, switch statements, use of eval
and parsing integers.
Built-in Prototypes
We should never mess with prototypes of built-in constructors like Array
or Function
.
If we need extra functionality that they don’t provide, we either make our own class or function.
Messing with built-in prototypes will bring problems with conflicts and people will be confused about why something is added to the prototype or why some methods act differently than from the docs.
Properties that we add to the prototype might also show up in loops that don’t use hasOwnProperty
, which is even more confusing.
For instance, we should never write something like:
Array.prototype.foo = function(){
//...
}
Instead, we write our own function to pass in an array:
function(arr) {
//...
}
switch Pattern
We can use switch statements to replace a series of if
and else
statements.
For instance, we can write:
let val = 0,
result = '';
switch (val) {
case 0:
result = "zero";
break;
case 1:
result = "one";
break;
default:
result = "unknown";
}
instead of:
let val = 0,
result = '';
if (val === 0) {
result = 'zero';
} else if (val === 1) {
result = 'one';
} else {
result = 'unknown';
}
It’s longer but more readable.
We’ve to remember to add break
to each case
statement so that once a matching case is found, it’ll stop running the other cases.
Also, we should end with a default
case so that we do something even if no matching value is found.
Avoiding Implied Typecasting
To avoid implied typecasting, we should avoid using the ==
operator for equality comparison.
The rules are complex and confusing for type conversions.
Instead, we should use the ===
operator for comparison:
const foo = 0;
if (foo === false) {
// not running because foo is 0, not false
}
The type of both operands have to be the same before comparison, so the body won’t run.
Likewise, we use the !==
operator for inequality comparison.
So we write:
const foo = 0;
if (foo !== false) {
// runs because foo is 0, not false
}
If we use the <
, >
, <=
, or >=
operators, then we’ve to convert the types of the operators to the same types explicitly to avoid confusion.
Avoiding eval()
eval
should be avoided. Security issues arise from running code from strings.
It’s also very hard to debug code that is in strings.
Also, browsers can’t optimize code that is in strings.
Therefore, we should never use it.
setInterval
, setTimeout
, and Function
constructor all take strings.
So we shouldn’t use the Function
constructor to define functions.
If we call setInterval
or setTimeout
, the callback should be a function and not a string.
So instead of writing:
setTimeout(`console.log('hello')`)
We write:
setTimeout(() => console.log('hello'))
Number Conversions with parseInt()
We can call parseInt
to get a numeric value from a string.
It takes a 2nd radix parameter to specify the base of the number.
We should include the 2nd argument so we make sure that the number string is converted properly to a number.
For instance, we should write:
const num = parseInt("16", 10);
so that we know that 16 is a decimal number.
However, we should be careful with parseInt
since it’ll try to convert strings with numbers and letters.
For instance, parseInt(“1 abc”)
returns 1.
We can also use the +
operator to convert something to a number by writing:
const num = +"16";
On the other hand, +”1 abc”
return NaN
, which is probably what we want.
Conclusion
We should never add methods to built-in prototypes.
It just confuses people and it may overwrite existing methods, which is no good.
switch
statements may be good alternatives to if and else.
We shouldn’t use eval
or related methods like the Function
constructor or passing strings to setTimeout
or setInterval
.